
import math
from huobi.client.account import AccountClient
from huobi.client.generic import GenericClient
from huobi.client.market import MarketClient
from huobi.client.trade import TradeClient
from huobi.constant.definition import CandlestickInterval, OrderState, OrderType
from huobi.model.market import trade_detail_event
from huobi.model.trade.order_update_event import OrderUpdateEvent
from huobi.model.market.trade_detail_event import TradeDetailEvent
from huobi.utils.log_info import Log
from callback import on_error_handler
from quant_define import QuantState
from symbols.quant_symbol import QuantSymbol

max_amount = 6.0
amount_percent = 0.7
class QuantSymbolNewSymbol(QuantSymbol):

    def __init__(self, account_client:AccountClient, generic_client:GenericClient,
         trade_client:TradeClient, market_client:MarketClient) -> None:
        super().__init__(account_client,generic_client,trade_client,market_client)
        self.m_state:QuantState = QuantState.WaitBuy
        self.m_buy_orderid = None
        self.m_sell_orderid = None
        self.m_to_sell_amount = 0

        self.m_sub_trade_detail_service = None
        self.m_get_candles_tick = False
        self.m_try_buy_count = 0
        self.m_try_sell_count = 0

    def update(self):
        super().update()

        if self.m_state == QuantState.WaitBuy:
            self.buy()
        
        if self.m_state == QuantState.Buying:
            self.check_buy_order()

        if self.m_state == QuantState.Subscribe:
            self.subscribe()

        if self.m_state == QuantState.Selling:
            self.check_sell_order()

    def buy(self):
        min_amount = float(self.m_symbol.min_order_value)
        balance = self.get_balance("usdt")
        if min_amount > balance:
            Log.info("[buy]: blance not enough blance = {0}".format(balance))
            self.complete()
            return

        amount = balance * amount_percent
        amount = max(amount,min_amount)
        if min_amount < max_amount:
            amount = min(amount,max_amount)
        amount = math.ceil(amount)
        self.m_buy_orderid = self.create_order(self.m_symbol.symbol,0,amount,OrderType.BUY_MARKET)
        Log.info("[buy]: go to buying state symbol = {0} orderid = {1}".format(self.m_symbol.symbol,self.m_buy_orderid))
        self.m_state = QuantState.Buying
            
    def subscribe(self):
        
        if self.m_sub_trade_detail_service is None:
            self.m_sub_trade_detail_service = self.m_market_client.sub_trade_detail(self.m_symbol.symbol,self.on_trade_detail_callback,on_error_handler)
            Log.info("[subscribe]: sub trade detail symbol={0}".format(self.m_symbol.symbol))
        
        if not self.m_get_candles_tick:
            candlesticks = self.m_market_client.get_candlestick(self.m_symbol.symbol,CandlestickInterval.DAY1,1)
            for candlestick in candlesticks:
                self.m_high_price = candlestick.high
                Log.info("[subscribe]: high_price = {0} symbol = {1}".format(self.m_high_price,self.m_symbol.symbol))
                break
            self.m_get_candles_tick = True
        
        if (self.m_sub_trade_detail_service is not None) and self.m_get_candles_tick:
            self.m_state = QuantState.WaitSell
            Log.info("go to WaitSell state symbol = {0}".format(self.m_symbol.symbol))

    def check_buy_order(self):
        order = self.m_trade_client.get_order(self.m_buy_orderid)
        if order is not None and \
            order.finished_at>0 and \
            float(order.filled_amount)>0:

                self.m_buy_amount = float(order.filled_amount) - float(order.filled_fees)
                self.m_buy_cash = float(order.filled_cash_amount)
                self.m_buy_price = self.m_buy_cash / self.m_buy_amount
                self.m_ts = int(order.created_at)

                Log.info("[check_buy_order]: buy order id filled orderid = {0}, filled_amount={1}, filled_fees={2}, \
                    filled_cash={3}, calc_price={4}".format(self.m_buy_orderid,order.filled_amount,order.filled_fees, \
                    order.filled_cash_amount,self.m_buy_price))

                own_amount = self.get_balance(self.m_symbol.base_currency)
                own_amount = self.to_precision_amount(own_amount,self.m_symbol.amount_precision)
                self.m_to_sell_amount = own_amount
                Log.info("[check_buy_order]: to_sell_amount = {0}".format(self.m_to_sell_amount))
                self.m_state = QuantState.Subscribe
                Log.info("[check_buy_order]: go to state Subscribe")
        else:
            if self.m_try_buy_count<3:
                Log.error("[check_buy_order]: create failed try again order id = {0}".format(self.m_buy_orderid))
                self.m_try_buy_count = self.m_try_buy_count + 1
                self.m_state = QuantState.WaitBuy
                self.update()
            else:
                Log.error("[check_buy_order]: create failed many times cancel buy")
                self.complete()
        

    def check_sell_order(self):
        order = self.m_trade_client.get_order(self.sell_orderid)
        if order is not None and \
            order.finished_at>0 and \
            float(order.filled_amount)>0:
                self.m_sell_amount = float(order.filled_amount)  - float(order.filled_fees)
                self.m_sell_cash = float(order.filled_cash_amount)
                self.m_sell_price = self.m_sell_cash / self.m_sell_amount
                self.m_state = QuantState.Selled

                Log.info("[check_sell_order]: sell order id filled orderid = {0}, filled_amount={1}, filled_fees={2}, \
                    filled_cash={3}, calc_price={4}".format(self.m_sell_orderid,order.filled_amount,order.filled_fees, \
                    order.filled_cash_amount,self.m_sell_price))

                self.complete()
        else:
            if self.m_try_sell_count<100:
                self.m_try_sell_count = self.m_try_sell_count + 1
                Log.error("[check_sell_order]: create failed try again order id = {0}, times={1}".format(self.m_buy_orderid,self.m_try_sell_count))
                self.create_order(self.m_symbol.symbol,0,self.m_to_sell_amount,OrderType.SELL_MARKET)
                self.m_state = QuantState.Selling
                Log.info("[check_sell_order]: go to Selling state symbol = {0}".format(self.m_symbol.symbol))
                self.update()
            else:
                Log.error("[check_sell_order]: create failed many times cancel sell")
                self.complete()
                

    def on_order_update_callback(self, order_update_event: 'OrderUpdateEvent'):
        symbol = self.ch2symbol(order_update_event.ch)
        if symbol  == self.m_symbol.symbol:
            status = order_update_event.data.orderStatus 
            price = order_update_event.data.tradePrice
            amount = order_update_event.data.tradeVolume
            #购买订单成交
            if order_update_event.data.type.find("buy")>=0:
                # 什么都不干了，因为成交提交订单的时候，还没订阅上，导致有部分否买订单没有检测到
                pass

            #卖出订单成交
            elif order_update_event.data.type.find("sell")>=0:
                if (status == OrderState.FILLED) or (status == OrderState.PARTIAL_FILLED):
                    Log.info("sub sell order fill: orderid = {0}, price={1}, amount={2}, status={3}".format(
                        order_update_event.data.orderId,price,amount,str(status)
                    ))

    def on_trade_detail_callback(self,trade_event: 'TradeDetailEvent'):
        symbol = self.ch2symbol(trade_event.ch)
        if symbol == self.m_symbol.symbol:
            # 更新最高价格
            for trade_detail in trade_event.data:
                cur_price = float(trade_detail.price)
                if cur_price > self.m_high_price:
                    self.m_high_price = cur_price
                if self.m_state == QuantState.WaitSell: 

                    sell = False
                    reason = ""
                    if cur_price>self.m_buy_price:
                        #小于最高价80% 卖出
                        if cur_price<=self.m_high_price*0.8:
                            sell = True
                            reason = "sell_小于最高价80%，尝试卖出"
                        #持有超过2分钟卖
                        elif trade_detail.ts - self.m_ts >= 2*60*1000:
                            if cur_price<=self.m_high_price*0.9:    
                                sell = True
                                reason = "sell_上架超过2分钟,且小于最高价90%，尝试卖出"
                        elif trade_detail.ts - self.m_ts > 60*60*1000:
                            sell = True
                            reason = "sell_持有超过1小时，且盈利，直接卖出"
                    elif trade_detail.ts - self.m_ts >= 30*60*1000:
                        sell = True
                        reason = "sell_上架超过半小时且亏损，尝试卖出"
                    if sell:
                        total_value = cur_price*self.m_to_sell_amount
                        Log.info("[on_trade_detail_callback]:"+str(reason)+": sell cur_price = {0} count = {1}, total = {2}",cur_price,self.m_to_sell_amount,total_value)
                        if total_value > float(self.m_symbol.min_order_value):
                            Log.info("[on_trade_detail_callback]: go to Selling state symbol = {0}".format(self.m_symbol.symbol))
                            self.create_order(symbol,0,self.m_to_sell_amount,OrderType.SELL_MARKET)
                            self.m_state = QuantState.Selling
                        else:
                            Log.info("[on_trade_detail_callback]: 因金额小于最小交易金额，暂停卖出")
                            self.m_state = QuantState.ImmediateSell
                elif self.m_state == QuantState.ImmediateSell:
                    total_value = cur_price*self.m_to_sell_amount
                    if total_value > float(self.m_symbol.min_order_value):
                        self.create_order(symbol,0,self.m_to_sell_amount,OrderType.SELL_MARKET)
                        self.m_state = QuantState.Selling
                        Log.info("[on_trade_detail_callback]:立即卖出 sell cur_price = {0} count = {1}, total = {2}",cur_price,self.m_to_sell_amount,total_value)

    def complete(self):
        self.set_die(True)
        if self.m_sub_trade_detail_service is not None:
            self.m_sub_trade_detail_service.unsubscribe_all()
        Log.info("[complete]: complete symbol = {0}".format(self.m_symbol.symbol))

    # 转化为有效位数的数量
    def to_precision_amount(self, amount, precision):
        precision = math.pow(10,precision)
        pre_amount = float(math.floor(amount*precision))/float(precision)
        while pre_amount > amount:
            pre_amount -= 1/precision

        return pre_amount

